//////////////////////////////////////////////////////////////////
// Includes
//
#ifndef _THE_SCENE_H_
#define _THE_SCENE_H_

#include <sstream>
#include <algorithm>
#include <list>
#include <time.h>
#include <vector>

#include "cglx.h"
#include "foreground.h"
#include "background.h"
#include "weather/weatherparser.h"
#include "utility.h"
#include "util/vec3f.h"

// File to read weather info from, file should only contain weather code and 
// temp separated by a newline
#define WEATHER_FILE "weather/weather_test.txt"

// How long the intervals are before the weather is read from file.
//      60 * 10 equivalent to ten minutes
// Should keep this value high since updating requires a (slow) file I/O
#define SECONDS_TO_UPDATE_WEATHER 60 * 0.10

//////////////////////////////////////////////////////////////////
// Class Declaration
//
class Scene
{
private:
	GLfloat myAspectRatio;
    
    WeatherParser *myWeatherParser;
    weather_t *myWeather;
	
	Background *myBackground;
	
    /**
     * Foreground consists of everything that responds to camera input.
     * Also, imageprocessor is in myForeground
     */
	Foreground *myForeground;

	Color4f myAmbientLightColor;
	Vec3f mySunPos;

	struct tm *myTimeInfo;
	
	/**
	 * Counts number of seconds so we know when to get new weather info.
	 */
    GLfloat myFrameTimer;
	
	
public:

	/**
	 * Allow subclasses to properly dispose of themselves.
	 */
	virtual ~Scene ()
	{
		delete myBackground;
		delete myForeground;
        delete myWeather;
        delete myWeatherParser;
		delete myTimeInfo;
	}


	/*
	 * Initialize general OpenGL values once (in place of constructor).
	 *
	 * This includes, for example, light and texture parameters.
	 */
	virtual void init (GLfloat aspectRatio, int argc, char * argv[])
	{
		myAspectRatio = aspectRatio;
        myWeatherParser = new WeatherParser(WEATHER_FILE);
        myWeather = new weather_t;
        *myWeather = myWeatherParser->getWeatherEnum();
        myFrameTimer = 0;
		
		//############################# Background
		myBackground = new Background(myWeather);
		
		//############################# Foreground
		myForeground = new Foreground(myWeather, myBackground->getTerrain());

		//############################# Lighting
		// Ambient light color
		myTimeInfo = Utility::getTime();
		if(myTimeInfo->tm_hour == 6 || myTimeInfo->tm_hour == 18)
		{
			GLfloat newAmbientLight = 2.0f - (1.0f/60.0f)*myTimeInfo->tm_min;
			myAmbientLightColor.set(newAmbientLight, newAmbientLight, newAmbientLight);
		}
		else if(myTimeInfo->tm_hour < 6 || myTimeInfo->tm_hour > 18)
		{
			myAmbientLightColor.set(1.0f, 1.0f, 1.0f);
		}
		else
		{
			myAmbientLightColor.set(2.0, 2.0, 2.0);
		}
		GLfloat sun_pos_x = (5.0f/18.0f) * (myTimeInfo->tm_hour*60.0f + myTimeInfo->tm_min) - 200.0f;
		GLfloat sun_pos_y = -(1.0f/100.0f) * pow(sun_pos_x, 2) + 100.0f;
		mySunPos = Vec3f(sun_pos_x, sun_pos_y, 0.0f);

		glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_COLOR_MATERIAL);
	}


	/*
	 * Set camera's view of scene.
	 */
	virtual void setCamera ()
	{
		gluLookAt(0, 5, 15,      // from
				  0, 5, 0,      // to
				  0, 1, 0);     // up
	}


	/*
	 * Display all objects in the scene.
	 *
	 * Lighting values should generally be set first, before scene objects.
	 */
	virtual void display ()
	{   
		// Diffuse Lighting (This seems like it doesn't do anything)
        static GLfloat GL_LIGHT0_POS[4] = {mySunPos[0], mySunPos[1], mySunPos[3], 1.0};
        glLightfv(GL_LIGHT0, GL_POSITION, GL_LIGHT0_POS);
        static GLfloat GL_LIGHT0_COLOR[4] = { 0.76, 0.8, 0.8, 1 };
        glLightfv(GL_LIGHT0, GL_DIFFUSE, GL_LIGHT0_COLOR);

		// Ambient Lighting
		glLightModelfv(GL_LIGHT_MODEL_AMBIENT, myAmbientLightColor.getColorsArray());
		
		//############################### Draw the scene
		myBackground->draw();
		myForeground->draw();
    }


	/*
	 * Update scene values to be reflected in next call to display.
	 *
	 * In this way, small changes to scene will produce smooth animation.
	 */
	virtual void update (GLfloat elapsedTime)
	{
		myBackground->update();
		myForeground->update(elapsedTime);

		/*
		   TODO consider updating time less often
		 */
		myTimeInfo = Utility::getTime();
		if(myTimeInfo->tm_hour == 6 || myTimeInfo->tm_hour == 18)
		{
			GLfloat newAmbientLight = 2.0f - (1.0f/60.0f)*myTimeInfo->tm_min;
			myAmbientLightColor.set(newAmbientLight, newAmbientLight, newAmbientLight);
		}
			
		// To access min: myTimeInfo->tm_min, hours: myTimeInfo->tm_hour
		
        if (myFrameTimer > SECONDS_TO_UPDATE_WEATHER) {
            *myWeather = myWeatherParser->getWeatherEnum();
            myFrameTimer = 0; // Reset frame timer
        } else {
            myFrameTimer += elapsedTime;
        }
		GLfloat sun_pos_x = (5.0f/18.0f) * (myTimeInfo->tm_hour*60.0f + myTimeInfo->tm_min) - 200.0f;
		GLfloat sun_pos_y = -(1.0f/400.0f) * pow(sun_pos_x, 2) + 100.0f;
		mySunPos *= 0.0f;
		mySunPos += Vec3f(sun_pos_x, sun_pos_y, 0.0f);
	}
	
//	static Vec3f getSunPosition()
//	{
//		return mySunPos;
//	}


	/*
	 * Called when one or more objects have been clicked on by user.
	 *
	 * selectInfo format for numSelected objects is:
	 *   [num names given object], [zfront], [zback], [name_0, name_1, ...]
	 */
	void selectObject (GLuint numSelected, GLuint selectInfo[])
	{
		// by default, do nothing
	}


	/*
	 * Called when key is pressed.
	 *
	 * 'key' parameter is ASCII value of the alphanumeric key, if it exists.
	 * 'specialKey' is value of key that DOES NOT generate an ASCII value.
	 *    e.g.: F1-F12, Page-up, Page-down Arrow-keys, etc.
	 * 'x' and 'y' parameters are current location of mouse
	 *    (in window-relative coordinates)
	 */
	virtual void keyPressed (GLubyte key, GLint specialKey, GLint x, GLint y)
	{
	}


	/*
	 * Called when mouse moves within window.
	 *
	 * 'x' and 'y' parameters are current location of mouse
	 *  (in window-relative coordinates)
	 */
	virtual void mouseMotion (GLint x, GLint y)
	{
		// GLfloat targetX = (((GLfloat)x/960.0) * 50.0) - 25;
		// myCloud->setTargetX(targetX);
	}


	/*
	 * Called when mouse moves within window and one of the buttons is pressed.
	 *
	 * 'x' and 'y' parameters are current location of mouse
	 *  (in window-relative coordinates)
	 */
	virtual void mouseDrag (GLint x, GLint y)
	{
		// by default, do nothing
	}


	/*
	 * Called when a mouse button is pressed or released.
	 *
	 * 'button' parameter is one of:
	 *    GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, or GLUT_RIGHT_BUTTON.
	 * 'state' parameter is one of:
	 *    GLUT_DOWN or GLUT_UP, if button was pressed or released
	 * 'x' and 'y' parameters are current location of mouse
	 *    (in window-relative coordinates)
	 */
	virtual void mouseButtonChanged (GLint button, GLint state,
			GLint x, GLint y)
	{
		// by default, do nothing
	}
};

#endif
